home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / BufferIO.c next >
Encoding:
Text File  |  1996-08-28  |  26.7 KB  |  610 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        BufferIO.c
  3.  
  4.     Contains:    Container Manager Buffered Data I/O Routines
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <3>     8/13/96    DM        1362809: disable containers on error
  15.          <2>     1/15/96    TJ        Cleaned Up
  16.          <2>     8/26/94    EL        #1182308 BufferIO should use value's
  17.                                     container and not the updating container to
  18.                                     determine endian-ness.
  19.          <1>      2/3/94    EL        first checked in
  20.  
  21.     To Do:
  22. */
  23.  
  24. /*---------------------------------------------------------------------------*
  25.  |                                                                           |
  26.  |                            <<<  BufferIO.c  >>>                           |
  27.  |                                                                           |
  28.  |               Container Manager Buffered Data I/O Routines                |
  29.  |                                                                           |
  30.  |                               Ira L. Ruben                                |
  31.  |                                  8/3/92                                   |
  32.  |                                                                           |
  33.  |                     Copyright Apple Computer, Inc. 1992-1994              |
  34.  |                           All rights reserved.                            |
  35.  |                                                                           |
  36.  *---------------------------------------------------------------------------*
  37.  
  38.  This file contains the routines to allocate and free I/O buffers used for buffered reading
  39.  and writing special data in "chunks" of 1, 2, and 4 byte quantities.  This is NOT a
  40.  generalized I/O package that handles arbitrary sized data.  It is intended as an interface
  41.  to the 1, 2, and 4 byte formatting and extraction handlers for portably and efficiently
  42.  handling internal information written to the container.  This includes, for example,
  43.  updating instructions and TOC entries.  
  44.  
  45.  The main use of these I/O routines is to buffer this internal data as value data for
  46.  some value.  Thus in the case of updating instructions, the updating information is
  47.  buffered as value data for an object's special "updating" property.
  48.  
  49.  Although the main use is to write value data for a value, the buffering routines also
  50.  support I/O directly to and from the container's I/O handlers.  The buffering algorithms
  51.  are basically the same.  The only difference is that in this case a value refNum is not
  52.  supplied.  An example of such a direct handler use would be TOC I/O.
  53.  
  54.  Multiple buffers of different sizes can be set up simultaneously for multiple values.  The
  55.  routines in this file take care of buffering and deblocking as necessary.  Obviously, if
  56.  the I/O handlers are being called directly, there can only be one such buffered use of
  57.  these handlers, unless care is taken to properly protect the container I/O position (seek) 
  58.  pointer(s).
  59.  
  60.  The use of the routines in this package follow a specific protocol as follows ("|" here
  61.  means "or" as an alternative):
  62.  
  63.  For input:
  64.  
  65.              if (setjmp(setJmpEnv)) {
  66.                 cmReleaseIOBuffer(ioBuffer);
  67.                 ERRORx(...)
  68.                 return (failure);
  69.             }
  70.             
  71.             ioBuffer = cmUseIOBuffer(container, bufferSize, &setJmpEnv);
  72.             
  73.             cmNewBufferedInputData(ioBuffer, valueRefNum | NULL, totalAmountToRead);
  74.             cmReadBufferedData(ioBuffer, 1 | 2 | 4);
  75.                        - - -
  76.             
  77.             cmReleaseIOBuffer(ioBuffer);
  78.  
  79.  For output:
  80.   
  81.              if (setjmp(setJmpEnv)) {
  82.                 cmReleaseIOBuffer(ioBuffer);
  83.                 ERRORx(...)
  84.                 return (failure);
  85.             }
  86.  
  87.             ioBuffer = cmUseIOBuffer(container, bufferSize, &setJmpEnv);
  88.             cmNewBufferedOutputData(ioBuffer, valueRefNum | NULL);
  89.             
  90.             cmWriteBufferedData(ioBuffer, 1 | 2 | 4);
  91.                        - - -
  92.             
  93.             cmFlushOutputBuffer(ioBuffer);
  94.             cmReleaseIOBuffer(ioBuffer);
  95.  
  96.  A setjmp/longjmp environment is defined for both input and output.  The I/O routines
  97.  use this technique rather than having to check for errors on each I/O call.  All I/O is
  98.  beacketed between a cmUseIOBuffer() and cmReleaseIOBuffer().  cmNewBufferedInputData() is
  99.  called for each new value to read to define that value and the total amount to read.
  100.  cmNewBufferedOutputData() just defines the value for output.  cmReadBufferedData() is 
  101.  used to read 1, 2, and 4 byte quantities while cmWriteBufferedData() is used to write
  102.  those quantities.  The write also requires a cmFlushOutputBuffer() at the end to make
  103.  sure the last buffer is written.
  104.  
  105.  There are a few other special calls defined here.  But the above protocal must be 
  106.  followed to do the I/O properly.
  107. */
  108.  
  109.  
  110. #include <stddef.h>
  111. #include <stdio.h>
  112. #include <string.h>
  113. #include <setjmp.h>
  114.  
  115. #ifndef __CMTYPES__
  116. #include "CMTypes.h"
  117. #endif
  118. #ifndef __CM_API_TYPES__
  119. #include "CMAPITyp.h"
  120. #endif
  121. #ifndef __CM_API__
  122. #include "CMAPI.h"
  123. #endif
  124. #ifndef __LISTMGR__
  125. #include "ListMgr.h"
  126. #endif
  127. #ifndef __TOCENTRIES__
  128. #include "TOCEnts.h"   
  129. #endif
  130. #ifndef __TOCOBJECTS__
  131. #include "TOCObjs.h"   
  132. #endif
  133. #ifndef __CONTAINEROPS__
  134. #include "Containr.h"  
  135. #endif
  136. #ifndef __HANDLERS__
  137. #include "Handlers.h"
  138. #endif
  139. #ifndef __SESSIONDATA__
  140. #include "Session.h"          
  141. #endif
  142. #ifndef __ERRORRPT__
  143. #include "ErrorRpt.h"      
  144. #endif
  145. #ifndef __UTILITYROUTINES__
  146. #include "Utility.h"        
  147. #endif
  148. #ifndef __BUFFEREDIO__
  149. #include "BufferIO.h"  
  150. #endif
  151.  
  152.                                                                     CM_CFUNCTIONS
  153.  
  154. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  155. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  156. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  157. /* choke compilers that don't recognize them.                */
  158.  
  159. #if CM_MPW
  160. #pragma segment BufferedIO
  161. #endif
  162.  
  163.  
  164. /* All I/O buffers are linked together on a chain attached to a container. The buffers    */
  165. /* are actually part of an "I/O control block" defined below.  This information is only    */
  166. /* known to this file.  All callers to the routines here only know it as an anonymous        */
  167. /* "void *" pointer.                                                                                                                                        */
  168.  
  169. struct IOControl {                                                /* Layout of an I/O control block:                        */
  170.     ListLinks                 ioLinks;                                /*    in-use buffers links (must be first)        */
  171.     TOCValueHdrPtr    theValueHdr;                        /*        refNum whose value data to do I/O on        */
  172.     ContainerPtr         container;                            /*        (updating) container control block ptr    */
  173.     CM_LONG                     maxBufSize;                            /*        max size of the buffer                                    */
  174.     CM_LONG                  bufferPosition;                    /*        0-rel. next byte to use in updateBuffer    */
  175.     CM_LONG                     bufferReadOffset;                /*        offset to next buffer to read in value    */
  176.     CM_ULONG                 dataSize;                                /*        total size of data to be read                        */
  177.     CM_ULONG                 ioPos;                                    /*        current container I/O position                    */
  178.     union {                                                                    /*        setjmp environment for buffered I/O            */
  179.         jmp_buf                jmpBuf;                                    /*              *** PORTABILITY PROBLEM ***                */
  180.         CM_CHAR                firstByte;                            /*       Don't assume the layout of a jmp_buf    */
  181.     } bufferIOEnv;                                                    /*             firstByte used to memcpy to  jmp_buf    */
  182.     CM_CHAR                  dataBuffer[1];                    /*         the START of the data I/O buffer                */
  183. };
  184. typedef struct IOControl IOControl, *IOControlPtr;
  185.  
  186. /* CAUTION: I/O error "recovery" here is done by using a longjmp on a setjmp environment*/
  187. /*                     passed to cmUseIOBuffer() below.  In order to save that environment, a             */
  188. /*                    memcpy() is done to copy the cmUseIOBuffer() parameter to the above struct.    */
  189. /*                    No assumptions are made about the layout of a setjmp's jmp_buf!  For                 */
  190. /*                    example, it is never assumed to be an array.  Therefore, in order to                 */
  191. /*                     PORTABLY pass the jmp_buf address to a memcpy(), a union is done to the         */
  192. /*                     first byte of the jmp_buf to address it.  To some "picky" compilers                 */
  193. /*                    addressing the first byte of a jmp_buf which IS an array as "&foo" is                */
  194. /*                    treated as an error (it expects  "&foo[0]" ).                                                                */
  195.  
  196.  
  197. /*----------------------------------------*
  198.  | cmUseIOBuffer - allocate an I/O buffer |
  199.  *----------------------------------------*
  200.  
  201.  This is used to allocate an I/O buffer and its associated control information.  The
  202.  buffer is used to buffer data for input or output using cmReadBufferedData() and
  203.  cmWriteBufferedData() respectively.
  204.  
  205.  The I/O control block pointer allocated is returned as the function result as an
  206.  anonymous "void *" pointer.  The caller should view this as a "buffer pointer" to be
  207.  passed to all the other routines in this file.
  208.  
  209.  An error is reported for allocation errors.  If the error reporter returns, NULL is
  210.  returned as the function result.
  211.  
  212.  The maximum size of the buffer to be allocated is passed.  A set setjmp/longjmp
  213.  environment variable is also passed for read and write error reporting and recovery.  The
  214.  setjmp/longjmp is used rather than have to check for errors on each I/O call of the
  215.  buffered I/O routines.
  216.   
  217.  Note, if the longjmp is taken, it is the caller's responsibility (in the setjmp code) to
  218.  report the error message.  This is done because the buffered I/O is used in a number of
  219.  contexts and a more appopriate error message can be reported by the caller.
  220.  
  221.  The I/O buffer control block allocated is added to a chain whose header is in the
  222.  updating container associated with the specified container.  The buffer should be 
  223.  released when is't use is no longer needed by calling cmReleaseIOBuffer().
  224. */
  225.  
  226. void *cmUseIOBuffer(ContainerPtr container, long maxBufferSize, jmp_buf *ioEnv)
  227. {
  228.     IOControlPtr io;
  229.     
  230.     /* The actual buffer allocated is 4 greater than the size required. This is enough of */
  231.     /* a pad (actually we use sizeof(CM_ULONG)) to allow cmWriteBufferData to always have    */
  232.     /* room to write the data just added.                                                                                                    */
  233.     
  234.     if ((io = (IOControlPtr)CMmalloc(sizeof(IOControl) + maxBufferSize +  sizeof(CM_ULONG))) == NULL) {
  235.         if (container->updatingContainer)
  236.             Container_Disable(container->updatingContainer);
  237.         Container_Disable(container);
  238.         ERROR1(CM_err_NoDataBuffer, CONTAINERNAME);
  239.         return (NULL);
  240.     }
  241.     
  242.     io->container             = container;                                        /* remember the container                        */
  243.     io->maxBufSize         = maxBufferSize;                                /* remember how big a buffer we got    */
  244.     memcpy(&io->bufferIOEnv.firstByte, ioEnv, sizeof(jmp_buf)); /* remember setjmp env.        */
  245.     
  246.     io->bufferPosition = 0;                                                        /* init buffer index for output            */
  247.     
  248.     return ((void *)cmAppendListCell(&io->container->updatingContainer->activeIOBuffers, io));
  249. }
  250.  
  251.  
  252. /*--------------------------------------------------*
  253.  | cmReleaseIOBuffer - release (free) an I/O buffer |
  254.  *--------------------------------------------------*
  255.  
  256.  This is called to free a buffer, ioBuffer,  allocated by cmUseIOBuffer(). If the caller
  257.  "forgets" to call this routine, then when the container is closed, cmFreeAllIOBuffers()
  258.  will be called to make sure all the buffers are free for that container.
  259. */
  260.  
  261. void cmReleaseIOBuffer(void *ioBuffer)
  262. {
  263.     ContainerPtr container;
  264.     
  265.     if (ioBuffer != NULL) {
  266.         container = ((IOControlPtr)ioBuffer)->container->updatingContainer;
  267.         CMfree(cmDeleteListCell(&container->activeIOBuffers, ioBuffer));/*unlink/free buffer*/
  268.     }
  269. }
  270.  
  271.  
  272. /*---------------------------------------------------------------------*
  273.  | cmFreeAllIOBuffers - free all I/O buffers allocated for a container |
  274.  *---------------------------------------------------------------------*
  275.  
  276.  This is called at container close time to make sure all buffers allocated for that
  277.  container are released (freed).
  278. */
  279.  
  280. void cmFreeAllIOBuffers(ContainerPtr container)
  281. {
  282.     IOControlPtr io, next;
  283.     
  284.     io = (IOControlPtr)cmGetListHead(&container->updatingContainer->activeIOBuffers);
  285.  
  286.     while (io) {                                                                    /* free all buffers still chained...        */
  287.         next = (IOControlPtr)cmGetNextListCell(io);
  288.         CMfree(io);
  289.         io = next;
  290.     }
  291. }
  292.  
  293.  
  294. /*------------------------------------------------------------*
  295.  | cmNewBufferedOutputData - initialize for buffered writing  |
  296.  *------------------------------------------------------------*
  297.  
  298.  This conditions the output buffer, ioBuffer, previously allocated by cmUseIOBuffer(), for
  299.  initial writing of the value data for theValueHdr.  If theValueHdr is NULL, the container
  300.  is written directly with the container's output handler.  This must be called once after
  301.  cmUseIOBuffer() and before any other calls.
  302. */
  303.  
  304. void cmNewBufferedOutputData(void *ioBuffer, TOCValueHdrPtr theValueHdr)
  305. {
  306.     IOControlPtr io = (IOControlPtr)ioBuffer;
  307.         
  308.     io->bufferPosition = 0;                                                        /* init buffer index for output            */
  309.     io->theValueHdr    = theValueHdr;                                    /* remember refNum (if any)                    */
  310.     io->ioPos             = (theValueHdr != NULL) ? 0UL     /* remember where we are                        */
  311.                                                                                          : (CM_ULONG)CMftell(io->container->updatingContainer);
  312. }
  313.  
  314.  
  315. /*----------------------------------------------------------*
  316.  | cmWriteBufferedData - write 1, 2, or 4 bytes to a buffer |
  317.  *----------------------------------------------------------*
  318.  
  319.  This is called to do buffered writes of 1, 2, or 4 byte data to a container. It is "glue"
  320.  code in the sense that it is the interface to the "format data" handler.  That handler
  321.  actually places the bytes in the buffer.  The buffer used is one returned from a previous
  322.  call to cmUseIOBuffer() and passed as the ioBuffer parameter.
  323.  
  324.  A CM_UCHAR, CM_USHORT, or CM_ULONG is passed in data4 which the handler is expected to
  325.  format into the buffer as 1, 2, or 4 byte quantities respectively.  This is done for
  326.  portability reasons since the architecture we're running this code on might map
  327.  differently into standard container entities.
  328.  
  329.  The expected output size, 1, 2, or 4, is passed, and the value to put into the buffer in
  330.  data4 (always passed as a CM_ULONG).  The buffer is written as value data for the value
  331.  "refNum" passed to cmNewBufferedOutputData() (theValueHdr passed to
  332.  cmNewBufferedOutputData()).  If there is no value data (theValudHdr is NULL), then the
  333.  container's output handler is called directly.  Thus two levels of I/O are available; a
  334.  "high level" through a value refNum, and a "low level" directly using the handler.
  335.  
  336. If the output size is negative, use its absolutely value. This is use to denote
  337. endian-ness netural data.
  338.     
  339.  This routine handles writing the buffer as it fills.  If an error is detected, a longjmp
  340.  on the setjmp environment passed to cmUseIOBuffer() is taken.  It is that setjmp code
  341.  which is responsible for reporting the error message.  This is done because the buffered
  342.  I/O routines can be used in a number of contexts.  The caller can report a more
  343.  appropriate error message rather than a more generic one we would have to report from
  344.  here.
  345.  
  346.  Note, for direct handler writes, no seeks are done here.  It is assumed that the output
  347.  is positioned to what is to be written.  If other code does seeks, then that code should
  348.  call cmBufferedIOftell() to reseek to the proper output position.
  349. */
  350.  
  351. void cmWriteBufferedData(void *ioBuffer, CM_LONG size, CM_ULONG data4)
  352. {
  353.     IOControlPtr     io                = (IOControlPtr)ioBuffer;
  354.     ContainerPtr     container = io->container;
  355.     CM_UCHAR             data1;
  356.     CM_USHORT             data2;
  357.     CMPrivateData d;
  358.     CM_LONG                realSize = size;
  359.     
  360.     if (realSize < 0)                                                            /* use the absolute value                                */
  361.         realSize = -realSize;
  362.     
  363.     /* As documented in the handlers, a pointer to the data to be placed in the buffer is    */
  364.     /* passed, and the handler can assume that pointer is pointing to a CM_UCHAR if size     */
  365.     /* is 1, CM_USHORT if size is 2, and CM_ULONG if size if 4.  So, not to be liers, we     */
  366.     /* set up a pointer to exactly that.                                                                                                    */
  367.     
  368.     if (realSize == 1) {                                                    /* 1 ==> pointer to CM_UCHAR                        */
  369.         data1 = (CM_UCHAR)data4;
  370.         d = (CMPrivateData)&data1;
  371.     } else if (realSize == 2) {                                        /* 2 ==> pointer to CM_USHORT                        */
  372.         data2 = (CM_USHORT)data4;
  373.         d = (CMPrivateData)&data2;
  374.     } else                                                                                /* 4 ==> pointer to CM_ULONG                        */
  375.         d = (CMPrivateData)&data4;
  376.     
  377.     /* Now we can put the data into the buffer using the handler...                                                */
  378.     
  379.     CMformatData(container, io->dataBuffer + io->bufferPosition, size, d);
  380.     
  381.     /* Bump the buffer offset. The max offset imposed on the buffer is actully at least 4    */
  382.     /* bytes less than the true maximum buffer size allocated because we had bumped up the*/
  383.     /* allocation by 4 bytes.  This means that the next free byte    could be passed                    */ 
  384.     /* maxBufSize without overruning the buffer.  At that point we write the buffer as         */
  385.     /* (value) data and reset the offset.  By doing this we never have to worry about not */
  386.     /* being able to write 2 or 4 bytes to the buffer.  We also document in the handler     */
  387.     /* that we always guarantee enough space in the buffer. The artifical limit does that.*/
  388.     
  389.     io->bufferPosition += realSize;                            /* update offset to the next free byte        */
  390.     
  391.     /* If the buffer needs flushing, now's the time to do it...                                                        */
  392.     
  393.     if (io->bufferPosition >= io->maxBufSize)        /* flush if full                                                     */
  394.         cmFlushOutputBuffer(ioBuffer);
  395. }
  396.  
  397.  
  398. /*----------------------------------------------*
  399.  | cmFlushOutputBuffer - flush an output buffer |
  400.  *----------------------------------------------*
  401.  
  402.  This routine is called to make sure that an output buffer (associated with the ioBuffer)
  403.  is flushed, i.e., fully written.  This completes the value data for the value specified
  404.  to cmNewBufferedOutputData(), or simply writes the remaining partial buffer directly if
  405.  the output handler is to be used directly (see cmNewBufferedOutputData() for further
  406.  details).
  407.  
  408.  Note, internally, this routine is also called from cmWriteBufferedData() when the buffer
  409.  fills.
  410. */
  411.  
  412. void cmFlushOutputBuffer(void *ioBuffer)
  413. {
  414.     IOControlPtr     io = (IOControlPtr)ioBuffer;
  415.     ContainerPtr     container = io->container->updatingContainer;
  416.  
  417.     if (io->bufferPosition > 0) {                                                /* flush if somthing to flush            */
  418.         if (io->theValueHdr != NULL) {                                        /* high or low level I/O ?                */
  419.             CMWriteValueData((CMValue)io->theValueHdr, (CMPtr)io->dataBuffer,
  420.                                              (CMCount)io->theValueHdr->size, (CMSize)io->bufferPosition);
  421.             io->ioPos += io->bufferPosition;                                /* update output position                    */
  422.         } else if (CMfwrite(container, io->dataBuffer, sizeof(CM_UCHAR), io->bufferPosition) == (CMSize)io->bufferPosition) {
  423.             io->ioPos = (CM_ULONG)CMftell(container);    /* get "true" container position    */
  424.             if (io->ioPos > container->physicalEOF)                 /* make sure of EOF settings...        */
  425.                 container->physicalEOF = io->ioPos;
  426.             SetLogicalEOF(container->physicalEOF);
  427.         } else                                                                                        /* take longjmp if output error        */
  428.             longjmp(io->bufferIOEnv.jmpBuf, 1);
  429.                 
  430.         io->bufferPosition = 0;                                                        /* the buffer is now empty                */
  431.     }
  432. }
  433.  
  434.  
  435. /*----------------------------------------------------------*
  436.  | cmNewBufferedInputData - initialize for buffered reading |
  437.  *----------------------------------------------------------*
  438.  
  439.  This conditions an input buffer, ioBuffer, previously allocated by cmUseIOBuffer(), for
  440.  initial reading up to dataSize bytes from the value data for theValueHdr.  If theValueHdr
  441.  is NULL, the container will be read directly with the container's input handler.  The
  442.  first call to cmReadBufferedData() after this call will cause cmReadBufferedData() to
  443.  reload its buffer with "new" data.  For each new value refNum, call this routine to read
  444.  its data.  If there is no value refNum, call it once after cmUseIOBuffer().  Either way,
  445.  call it before doing the first cmReadBufferedData() or cmBufferedIOftell().
  446. */
  447.  
  448. void cmNewBufferedInputData(void *ioBuffer, TOCValueHdrPtr theValueHdr,
  449.                                                         CM_ULONG dataSize)
  450. {
  451.     IOControlPtr io = (IOControlPtr)ioBuffer;
  452.     
  453.     io->bufferPosition   =  (io->maxBufSize);                    /* pretend we're at end of buffer        */
  454.     io->bufferReadOffset = -(io->maxBufSize);                    /* 1st refNum offset to will be 0        */        
  455.     io->theValueHdr        = theValueHdr;                                /* remember refNum (if any)                    */
  456.     io->dataSize                 = dataSize;                                    /* and max amount to read                        */
  457.     io->ioPos               = (theValueHdr != NULL) ? 0UL/* remember where we are                        */
  458.                                                                                          : (CM_ULONG)CMftell(io->container->updatingContainer);
  459. }
  460.  
  461.  
  462. /*----------------------------------------------------------*
  463.  | cmReadBufferedData - read 1, 2, or 4 bytes from a buffer |
  464.  *----------------------------------------------------------*
  465.  
  466.  This is called to do buffered reads of data from the container.  It is "glue" code in the
  467.  sense that it is the interface to the "extract data" handler.  That handler actually
  468.  copies the bytes from the buffer.  The buffer used is one returned from a previous
  469.  call to cmUseIOBuffer() and passed as the ioBuffer parameter.
  470.  
  471.  The handler is expected to extract 1, 2, or 4 byte quantities from the buffer and place
  472.  them in a CM_UCHAR, CM_USHORT, or CM_ULONG respectively.  This is done for portability
  473.  reasons since the architecture we're running this code on might map differently into 
  474.  standard container entities.
  475.  
  476.  The buffer is read as value data for the passed value "refNum" passed to the most recent
  477.  call to cmNewBufferedInputData().  The expected input size, 1, 2, or 4, is passed, and
  478.  the value returned as the function result (always as a CM_ULONG).
  479.  
  480.  The expected input size, 1, 2, or 4, is passed, and the value returned as the function
  481.  result (always as a CM_ULONG).  The buffer is read as value data for value "refNum" 
  482.  passed to cmNewBufferedInputData() (theValueHdr passed to cmNewBufferedInputData()).  If
  483.  there is no value data (theValueHdr is NULL), then the container's input handler is
  484.  called directly.  Thus two levels of I/O are available; a "high level" through a value
  485.  refNum, and a "low level" directly using the handler.
  486.  
  487.  If the input size is negative, use its absolutely value. This is use to denote
  488.  endian-ness netural data.
  489.     
  490.  This routine handles reading the buffer as all its data is extracted.  If an error is
  491.  detected, a longjmp on the setjmp environment passed to cmUseIOBuffer() is taken.  It is
  492.  that setjmp code which is responsible for reporting the error message.  This is done
  493.  because the buffered I/O routines can be used in a number of contexts.  The caller can
  494.  report a more appropriate error message rather than a more generic one we would have to
  495.  report from here.
  496.  
  497.  Note, the caller MUST call cmNewBufferedInputData() prior to the first call of
  498.  cmReadBufferedData() to cause the buffer to be loaded the first time and to get the
  499.  value refNum.  cmNewBufferedInputData() should be used any time a new value refNum is to
  500.  be used to read new data. 
  501.  
  502.  Also note, for direct handler reads, no seeks are done here.  It is assumed that the
  503.  input is positioned to what is to be read.  If other code does seeks, then that code
  504.  should call cmBufferedIOftell() to reseek to the proper input position.
  505. */
  506.  
  507. CM_ULONG cmReadBufferedData(void *ioBuffer, CM_LONG size)
  508. {
  509.     IOControlPtr     io                = (IOControlPtr)ioBuffer;
  510.     ContainerPtr     container = io->container;
  511.     CM_UCHAR             data1;
  512.     CM_USHORT             data2;
  513.     CM_ULONG             data4;
  514.     CMPrivateData d;
  515.     CMSize                 bufSize, amountRead;
  516.     CM_LONG             nextPos, remaining;
  517.     CM_LONG                realSize = size;
  518.     
  519.     if (realSize < 0)                                                            /* use the absolute value                                */
  520.         realSize = -realSize;
  521.     
  522.     /* Compute the buffer offset of the NEXT byte we will read AFTER the 1, 2, or 4 bytes    */
  523.     /* we are about to read.  Knowing this tells us whether all the bytes we need are in     */
  524.     /* buffer.  If they are not the buffer must be reloaded.                                                            */
  525.     
  526.     nextPos = io->bufferPosition + realSize;                /* next byte to read after this one        */
  527.     
  528.     if (nextPos > io->maxBufSize) {                                    /* reload if all bytes not available    */
  529.         io->bufferReadOffset += io->bufferPosition;        /* next value offset                                    */
  530.         remaining = io->dataSize - io->bufferReadOffset;    /* data left to read                            */
  531.         bufSize     = (remaining > io->maxBufSize) ? (CMSize)io->maxBufSize : (CMSize)remaining;
  532.         
  533.         if (io->theValueHdr != NULL)
  534.             amountRead = CMReadValueData((CMValue)io->theValueHdr, (CMPtr)io->dataBuffer,
  535.                                                                      (CMCount)io->bufferReadOffset, bufSize);
  536.         else {
  537.             io->ioPos += io->bufferPosition;                        /* we will reread unprocessed stuff        */
  538.             CMfseek(container->updatingContainer, io->ioPos, kCMSeekSet);
  539.             amountRead = CMfread(container->updatingContainer, io->dataBuffer, sizeof(CM_UCHAR), bufSize);
  540.         }
  541.         
  542.         if (amountRead != bufSize || amountRead == 0) {
  543.             io->bufferReadOffset = io->dataSize;                 /* safety                                                            */
  544.             longjmp(io->bufferIOEnv.jmpBuf, 2);
  545.         }
  546.         
  547.         io->ioPos += amountRead;                                            /* update input position                            */
  548.         io->bufferPosition = 0;                                                /* reset the buffer offset                        */
  549.         nextPos = (CM_LONG)realSize;                                    /* reset the next offset after that        */
  550.     }
  551.     
  552.     /* As documented in the handlers, a pointer to the data to be extracted from the             */
  553.     /* buffer is passed, and the handler can assume that pointer is pointing to a CM_UCHAR*/
  554.     /* if size is 1, CM_USHORT if size is 2, and CM_ULONG if size if 4.  It is also             */
  555.     /* documented that the handler need not worry that all the bytes are available in the    */
  556.     /* buffer.  We took care of that problem above.                                                                                */
  557.  
  558.     if (realSize == 1)                                                             /* 1 ==> pointer to CM_UCHAR                    */
  559.         d = (CMPrivateData)&data1;    
  560.     else if (realSize == 2)                                                    /* 2 ==> pointer to CM_USHORT                    */
  561.         d = (CMPrivateData)&data2;
  562.     else                                                                                        /* 4 ==> pointer to CM_ULONG                    */
  563.         d = (CMPrivateData)&data4;
  564.     
  565.     /* Now we can extract the data from the buffer using the handler...                                        */
  566.         
  567.     CMextractData(container, io->dataBuffer + io->bufferPosition, size, d);
  568.     
  569.     /* The data is always returned as the function result as a CM_ULONG.  Thus, for size     */
  570.     /* 1 and 2, we must up-convert the value...                                                                                        */
  571.     
  572.     if (realSize == 1)
  573.         data4 = (CM_ULONG)data1;
  574.     else if (realSize == 2)
  575.         data4 = (CM_ULONG)data2;
  576.     
  577.     /* Set the offset to the next byte to read in the buffer and return the value...            */
  578.     
  579.     io->bufferPosition = nextPos;                                        /* offset to next byte to read                */
  580.     
  581.     return (data4);                                                                    /* give caller back data as a CM_ULONG*/
  582. }
  583.  
  584.  
  585. /*-------------------------------------------------*
  586.  | cmBufferedIOftell - return current I/O position |
  587.  *-------------------------------------------------*
  588.  
  589.  This returns the current I/O position from the time cmNewBufferedOutputData() or 
  590.  cmNewBufferedInputData() was called.  For I/O to value data, the position is a value
  591.  data offset (initially always 0).  For direct handler I/O, the position is a container
  592.  offset.  This was set at the time cmNewBufferedOutputData() or cmNewBufferedInputData()
  593.  was called.  The position is updated as buffers fill and are written or reloaded when
  594.  reading.
  595.  
  596.  This routine is needed when only when there is the possibility that other code might be
  597.  doing "seeks" to the same container behind the buffered I/O routine's back!  Code doing
  598.  such seeks must be able to reseek the container position according to the value returned
  599.  here.  For value refNums, this usually is not necessary, since everything is in terms of
  600.  the value data offset rather than a container offset. But this routine, for completeness,
  601.  will return the appropriate offset position the I/O wants to use.
  602. */
  603.  
  604. CM_ULONG cmBufferedIOftell(void *ioBuffer)
  605. {
  606.     return (((IOControlPtr)ioBuffer)->ioPos);
  607. }
  608.  
  609.                                                             CM_END_CFUNCTIONS
  610.